package com.nnnu.wsnackshop.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.nnnu.wsnackshop.exception.ObjectException;
import com.nnnu.wsnackshop.mapper.*;
import com.nnnu.wsnackshop.pojo.dto.*;
import com.nnnu.wsnackshop.pojo.entity.ActivityLogs;
import com.nnnu.wsnackshop.pojo.entity.UserRoles;
import com.nnnu.wsnackshop.pojo.entity.Users;
import com.nnnu.wsnackshop.pojo.entity.Members;
import com.nnnu.wsnackshop.pojo.vo.AdminUserVO;
import com.nnnu.wsnackshop.result.PageResult;
import com.nnnu.wsnackshop.service.IUsersService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.nnnu.wsnackshop.utils.JwtUtil;
import com.nnnu.wsnackshop.utils.PasswordUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.AuthorizationException;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.Map;
import java.util.HashMap;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author zk
 * @since 2025-05-14
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class UsersServiceImpl extends ServiceImpl<UsersMapper, Users> implements IUsersService {
    private final RolesMapper roleMapper;
    private final PermissionsMapper permissionMapper;
    private final UserRolesMapper userRolesMapper;
    private final ActivityLogsMapper logMapper;
    private final RedisTemplate<String, Object> redisTemplate;
    private final MembersMapper membersMapper;

    @Override
    public Set<String> getRoleStrings(String username) {
        // 从数据库查询角色名称集合
        Set<String> roles = roleMapper.selectRoleNamesByUsername(username);
        return roles != null ? roles : Collections.emptySet();
    }

    @Override
    public Set<String> getPermissionStrings(String username) {
        // 从数据库查询权限标识集合
        Set<String> perms = permissionMapper.selectPermissionNamesByUsername(username);
        return perms != null ? perms : Collections.emptySet();
    }

    @Override
    public PageResult<AdminUserVO> pageUsers(@NonNull AdminUserPageDTO dto) {
        Page<Users> page = new Page<>(dto.getPageNum(), dto.getPageSize());
        LambdaQueryWrapper<Users> qw = new LambdaQueryWrapper<>();
        if (dto.getUsername() != null && !dto.getUsername().isBlank()) {
            qw.like(Users::getUsername, dto.getUsername());
        }
        Page<Users> result = baseMapper.selectPage(page, qw);
        List<AdminUserVO> records = result.getRecords().stream().map(user -> {
            AdminUserVO vo = BeanUtil.copyProperties(user, AdminUserVO.class);
            Set<String> roles = roleMapper.selectRoleNamesByUserId(Long.valueOf(user.getId()));
            vo.setRoles(roles);
            vo.setCreatedAt(user.getCreatedAt().toString());
            vo.setUpdatedAt(user.getUpdatedAt().toString());
            return vo;
        }).collect(Collectors.toList());
        return PageResult.<AdminUserVO>builder().total(result.getTotal()).items(records).build();
    }

    @Override
    public AdminUserVO getUserDetail(Long id) {
        Users user = baseMapper.selectById(id);
        if (user == null) {
            throw new ObjectException("用户不存在");
        }
        AdminUserVO vo = BeanUtil.copyProperties(user, AdminUserVO.class);
        Set<String> roles = roleMapper.selectRoleNamesByUserId(id);
        vo.setRoles(roles);
        vo.setCreatedAt(user.getCreatedAt().toString());
        vo.setUpdatedAt(user.getUpdatedAt().toString());
        return vo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createUser(@NonNull AdminUserSaveDTO dto) {
        // 唯一性校验
        if (baseMapper.exists(new LambdaQueryWrapper<Users>().eq(Users::getUsername, dto.getUsername()))) {
            throw new ObjectException("用户名已存在");
        }
        Users user = BeanUtil.copyProperties(dto, Users.class);
        user.setPassword(PasswordUtil.hash(dto.getPassword()));
        user.setCreatedAt(LocalDateTime.now());
        user.setUpdatedAt(LocalDateTime.now());
        baseMapper.insert(user);

        if (dto.getRoleIds() != null && !dto.getRoleIds().isEmpty()) {
            // 分配角色
            dto.getRoleIds().forEach(rid -> userRolesMapper.insert(new UserRoles().setUserId(user.getId()).setRoleId(Math.toIntExact(rid))));
        }

        // 操作日志
        logMapper.insert(new ActivityLogs().setUserId(getCurrentAdminId()).setAction("create_user").setTargetTable("users").setTargetId(user.getId().toString()).setDescription("创建用户：" + user.getUsername()));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateUser(Long id, AdminUserSaveDTO dto) {
        Users user = baseMapper.selectById(id);
        if (user == null) {
            throw new ObjectException("用户不存在");
        }
        // 更新字段
        user.setUsername(dto.getUsername());
        user.setNickname(dto.getNickname());
        user.setPhone(dto.getPhone());
        user.setEmail(dto.getEmail());
        user.setAvatar(dto.getAvatar() != null ? dto.getAvatar() : user.getAvatar());

        // 如果前端传了密码，则更新
        if (dto.getPassword() != null && !dto.getPassword().isBlank()) {
            user.setPassword(PasswordUtil.hash(dto.getPassword()));
        }
        baseMapper.updateById(user);

        if (dto.getRoleIds() != null) {
            // 更新角色关联
            userRolesMapper.delete(new LambdaUpdateWrapper<UserRoles>().eq(UserRoles::getUserId, id));
            dto.getRoleIds().forEach(rid -> userRolesMapper.insert(new UserRoles().setUserId(Math.toIntExact(id)).setRoleId(Math.toIntExact(rid))));
        }

        // 操作日志
        logMapper.insert(new ActivityLogs().setUserId(getCurrentAdminId()).setAction("update_user").setTargetTable("users").setTargetId(id.toString()).setDescription("更新用户：" + user.getUsername()));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteUsers(List<Long> ids) {
        if (CollectionUtil.isEmpty(ids)) {
            throw new ObjectException("待删除用户ID列表不能为空");
        }
        // 校验每个 ID 是否存在
        List<Long> notExists = ids.stream().distinct().filter(id -> baseMapper.selectById(id) == null).toList();
        if (!notExists.isEmpty()) {
            throw new ObjectException("以下用户不存在，无法删除: " + notExists);
        }
        // 删除 user_roles 关联
        userRolesMapper.delete(new LambdaUpdateWrapper<UserRoles>().in(UserRoles::getUserId, ids));
        // 批量删除用户
        baseMapper.deleteBatchIds(ids);

        // 记录日志
        Integer adminId = getCurrentAdminId();
        ids.forEach(id -> logMapper.insert(new ActivityLogs().setUserId(adminId).setAction("delete_user").setTargetTable("users").setTargetId(id.toString()).setDescription("批量删除用户 ID：" + id)));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Users register(UserRegisterDTO registerDTO) {
        // 1. 校验密码一致性
        if (!registerDTO.getPassword().equals(registerDTO.getConfirmPassword())) {
            throw new ObjectException("两次输入的密码不一致");
        }

        // 2. 校验用户名、手机号、邮箱唯一性
        LambdaQueryWrapper<Users> uniqueCheckWrapper = new LambdaQueryWrapper<>();
        uniqueCheckWrapper.eq(Users::getUsername, registerDTO.getUsername()).or().eq(Users::getPhone, registerDTO.getPhone()).or().eq(Users::getEmail, registerDTO.getEmail());
        if (baseMapper.exists(uniqueCheckWrapper)) {
            throw new ObjectException("用户名、手机号或邮箱已存在");
        }

        // 3. 构建用户实体并保存
        Users user = new Users();
        BeanUtil.copyProperties(registerDTO, user);
        user.setPassword(PasswordUtil.hash(registerDTO.getPassword())); // 密码加密
        user.setCreatedAt(LocalDateTime.now());
        user.setUpdatedAt(LocalDateTime.now());
        baseMapper.insert(user);

        // 4. 记录注册日志（可选，根据需要添加）
        logMapper.insert(new ActivityLogs().setUserId(user.getId()) // 注册用户自己的ID
                .setAction("user_register").setTargetTable("users").setTargetId(user.getId().toString()).setDescription("用户注册: " + user.getUsername()));

        return user;
    }

    @Override
    public Users getCurrentUser() {
        String token = (String) SecurityUtils.getSubject().getPrincipal();
        if (token == null || token.isEmpty()) {
            throw new AuthorizationException("未登录或登录已失效");
        }

        // 优先从token中获取用户对象
        Users user = JwtUtil.getUserFromToken(token);
        if (user != null) {
            user.setPassword(null);
            return user;
        }

        // 如果token中没有用户对象，则从username获取
        String username = JwtUtil.getUsername(token);
        if (username == null || username.isEmpty()) {
            throw new AuthorizationException("无效的JWT令牌");
        }

        user = baseMapper.selectOne(new LambdaQueryWrapper<Users>().eq(Users::getUsername, username));
        if (user == null) {
            throw new AuthorizationException("用户不存在或已删除");
        }
        user.setPassword(null);
        return user;
    }

    /**
     * 将令牌加入黑名单
     *
     * @param token 要加入黑名单的令牌
     */
    private void addTokenToBlacklist(String token) {
        if (token != null && !token.isEmpty() && redisTemplate != null) {
            // 获取token的剩余有效期
            try {
                Claims claims = Jwts.parserBuilder().setSigningKey(JwtUtil.getSecretKey()).build().parseClaimsJws(token).getBody();

                Date expiration = claims.getExpiration();
                if (expiration != null) {
                    long ttl = expiration.getTime() - System.currentTimeMillis();
                    if (ttl > 0) {
                        // 将令牌加入黑名单，有效期与令牌剩余有效期一致
                        redisTemplate.opsForValue().set("token_blacklist:" + token, "revoked", ttl, TimeUnit.MILLISECONDS);
                        log.info("令牌已加入黑名单，剩余有效期: {}ms", ttl);
                    }
                }
            } catch (Exception e) {
                log.error("将令牌加入黑名单时发生错误", e);
            }
        } else {
            log.warn("无法将令牌加入黑名单：令牌为空或Redis未配置");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Users updateProfile(UserUpdateDTO updateDTO) {
        // 1. 获取当前用户和当前令牌
        String currentToken = (String) SecurityUtils.getSubject().getPrincipal();
        Users currentUser = getCurrentUser();
        if (currentUser == null) {
            throw new ObjectException("当前用户不存在或未登录");
        }

        // 2. 校验手机号和邮箱唯一性（如果修改且不为当前用户的手机号/邮箱）
        LambdaQueryWrapper<Users> uniqueCheckWrapper = new LambdaQueryWrapper<>();
        boolean isPhoneChanged = updateDTO.getPhone() != null && !updateDTO.getPhone().equals(currentUser.getPhone());
        boolean isEmailChanged = updateDTO.getEmail() != null && !updateDTO.getEmail().equals(currentUser.getEmail());

        if (isPhoneChanged && baseMapper.exists(uniqueCheckWrapper.eq(Users::getPhone, updateDTO.getPhone()))) {
            throw new ObjectException("手机号已被占用");
        }
        uniqueCheckWrapper = new LambdaQueryWrapper<>(); // 重置wrapper以避免OR逻辑累积
        if (isEmailChanged && baseMapper.exists(uniqueCheckWrapper.eq(Users::getEmail, updateDTO.getEmail()))) {
            throw new ObjectException("邮箱已被占用");
        }

        // 3. 更新用户信息
        BeanUtils.copyProperties(updateDTO, currentUser, "password"); // 避免直接复制密码
        currentUser.setUpdatedAt(LocalDateTime.now());
        baseMapper.updateById(currentUser);

        // 4. 同步更新会员信息表
        try {
            // 查找对应的会员记录
            LambdaQueryWrapper<Members> memberWrapper = new LambdaQueryWrapper<>();
            memberWrapper.eq(Members::getUserId, currentUser.getId());
            Members member = membersMapper.selectOne(memberWrapper);

            if (member != null) {
                // 更新会员信息
                boolean isUpdated = false;

                // 更新昵称
                if (updateDTO.getNickname() != null) {
                    member.setNickname(updateDTO.getNickname());
                    isUpdated = true;
                }

                // 更新头像
                if (updateDTO.getAvatar() != null) {
                    member.setAvatar(updateDTO.getAvatar());
                    isUpdated = true;
                }

                // 更新性别
                if (updateDTO.getGender() != null) {
                    member.setGender(updateDTO.getGender());
                    isUpdated = true;
                }

                if (isUpdated) {
                    member.setUpdatedAt(LocalDateTime.now());
                    membersMapper.updateById(member);
                    log.info("同步更新会员信息成功: userId={}", currentUser.getId());
                }
            } else {
                log.warn("未找到对应的会员记录: userId={}", currentUser.getId());
            }
        } catch (Exception e) {
            log.error("同步更新会员信息失败: {}", e.getMessage(), e);
            // 不抛出异常，不影响用户信息更新
        }

        // 5. 记录操作日志
        logMapper.insert(new ActivityLogs().setUserId(currentUser.getId()).setAction("update_profile").setTargetTable("users").setTargetId(currentUser.getId().toString()).setDescription("更新用户资料: " + currentUser.getUsername()));

        // 6. 将旧令牌加入黑名单
        addTokenToBlacklist(currentToken);

        // 隐藏敏感信息
        currentUser.setPassword(null);
        return currentUser;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changePassword(PasswordChangeDTO changeDTO) {
        // 1. 获取当前用户
        Users currentUser = getCurrentUser();
        if (currentUser == null) {
            throw new ObjectException("当前用户不存在或未登录");
        }

        // 2. 验证旧密码
        if (!PasswordUtil.verify(changeDTO.getOldPassword(), currentUser.getPassword())) {
            throw new ObjectException("旧密码不正确");
        }

        // 3. 校验新密码和确认密码一致性
        if (!changeDTO.getNewPassword().equals(changeDTO.getConfirmPassword())) {
            throw new ObjectException("两次输入的新密码不一致");
        }

        // 4. 更新密码
        currentUser.setPassword(PasswordUtil.hash(changeDTO.getNewPassword()));
        currentUser.setUpdatedAt(LocalDateTime.now());
        baseMapper.updateById(currentUser);

        // 5. 记录操作日志
        logMapper.insert(new ActivityLogs().setUserId(currentUser.getId()).setAction("change_password").setTargetTable("users").setTargetId(currentUser.getId().toString()).setDescription("用户修改密码: " + currentUser.getUsername()));

        // 6. 强制当前用户下线，使其重新登录以获取新的令牌（可选，但推荐）
        SecurityUtils.getSubject().logout();
    }

    /**
     * 从当前登录的 JWT Subject 中提取管理员 ID
     */
    private Integer getCurrentAdminId() {
        String token = (String) SecurityUtils.getSubject().getPrincipal();
        String username = JwtUtil.getUsername(token);
        Users admin = baseMapper.selectOne(new LambdaQueryWrapper<Users>().eq(Users::getUsername, username));
        if (admin == null) {
            throw new AuthorizationException("当前用户未登录或已失效");
        }
        return admin.getId();
    }

    @Override
    public Map<String, Object> getUserPoints(Integer userId) {
        log.info("获取用户积分: userId={}", userId);

        Map<String, Object> result = new HashMap<>();

        // 查询会员信息
        LambdaQueryWrapper<Members> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Members::getUserId, userId);
        Members member = membersMapper.selectOne(queryWrapper);

        if (member != null) {
            result.put("points", member.getPoints() != null ? member.getPoints() : 0);
            result.put("growthValue", member.getGrowthValue() != null ? member.getGrowthValue() : 0);
            result.put("memberLevel", member.getMemberLevelId() != null ? member.getMemberLevelId() : 1);
        } else {
            // 如果会员信息不存在，返回默认值
            result.put("points", 0);
            result.put("growthValue", 0);
            result.put("memberLevel", 1);
        }

        return result;
    }
}
